package ink.charon.novel.service.impl;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import ink.charon.novel.constant.SpiderConstant;
import ink.charon.novel.entity.*;
import ink.charon.novel.utils.HttpUtil;
import ink.charon.novel.mapper.BookCategoryMapper;
import ink.charon.novel.mapper.SpiderSourceMapper;
import ink.charon.novel.service.BookListSpiderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class BookListSpiderServiceImpl implements BookListSpiderService {

    private final BookCategoryMapper bookCategoryMapper;

    private final ObjectMapper objectMapper;

    private final SpiderSourceMapper spiderSourceMapper;

    @Autowired
    public BookListSpiderServiceImpl(ObjectMapper objectMapper, SpiderSourceMapper spiderSourceMapper, BookCategoryMapper bookCategoryMapper) {
        this.objectMapper = objectMapper;
        this.spiderSourceMapper = spiderSourceMapper;
        this.bookCategoryMapper = bookCategoryMapper;
    }

    public List<Book> getPageBookInfo(Page page, SpiderSource spiderSource, int catId, Site site){
        List<Book> books = new ArrayList<>();
        SpiderRule spiderRule = null;
        try {
            spiderRule = objectMapper.readValue(spiderSource.getSpiderRule(), SpiderRule.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        assert spiderRule != null;
        List<Map<String, String>> bookListDivRule = spiderRule.getBookListRule();
        for (Map<String, String> divRule : bookListDivRule) {
            String updateTime = divRule.get(SpiderConstant.UPDATE_TIME);
            String timePatten = divRule.get(SpiderConstant.TIME_PATTEN);
            List<Selectable> nodes = genSelectable(page.getHtml(), divRule.get(SpiderConstant.DIV_RULE)).nodes();
            for (Selectable node : nodes) {
                Book book = new Book();
                BookCategory bookCategory = bookCategoryMapper.selectById(catId);
                book.setCatId(catId);
                book.setWorkDirection(bookCategory.getWorkDirection());
                book.setCatName(bookCategory.getName());
                String bookName = genSelectable(node, divRule.get(SpiderConstant.BOOK_NAME)).get();
                book.setListBookName(bookName);
                String detailUrl = genSelectable(node, divRule.get(SpiderConstant.DETAIL_URL)).get();
                book.setDetailUrl(detailUrl);
                String desc = genSelectable(node, divRule.get(SpiderConstant.DESC)).get();
                book.setListBookDesc(desc);
                String author = genSelectable(node, divRule.get(SpiderConstant.AUTHOR)).get();
                book.setListBookAuthor(author);
                String pic = genSelectable(node, divRule.get(SpiderConstant.COVER)).get();
                book.setListPicUrl(pic);
                //0,连载中;1,已完结
                String status = genSelectable(node, divRule.get(SpiderConstant.STATUS)).get();
                String[] statusRules = divRule.get(SpiderConstant.STATUS_RULE).split(SpiderConstant.SEMICOLON);
                String[] statusRule0 = statusRules[0].split(SpiderConstant.COMMA);
                String[] statusRule1 = statusRules[1].split(SpiderConstant.COMMA);
                byte listBookStatus = 0;
                if (statusRule0[1].equals(status)) {
                    listBookStatus = new Byte(statusRule0[0]);
                } else if (statusRule1[1].equals(status)) {
                    listBookStatus = new Byte(statusRule1[0]);
                }
                book.setListBookStatus(listBookStatus);
                String wordCount = genSelectable(node, divRule.get(SpiderConstant.WORD_COUNT)).get();
                book.setListBookWordCount(wordCount);
                Date upDate = null;
                String dateStr = genSelectable(node, updateTime).get();
                try {
                    String tmpTimePatten = timePatten;
                    if (!tmpTimePatten.contains("yyyy")) {
                        tmpTimePatten = "yyyy " + tmpTimePatten;
                        dateStr = Calendar.getInstance().get(Calendar.YEAR) + " " + dateStr;
                    }
                    upDate = new SimpleDateFormat(tmpTimePatten).parse(dateStr);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                book.setListLastUpdateTime(upDate);
                String visitCount = genSelectable(node, divRule.get(SpiderConstant.VISIT_COUNT)).get();
                book.setListVisitCount(visitCount);
                String weekClick = genSelectable(node, divRule.get(SpiderConstant.WEEK_CLICK)).get();
                book.setListWeekClick(weekClick);
                String monthClick = genSelectable(node, divRule.get(SpiderConstant.MONTH_CLICK)).get();
                book.setListMonthClick(monthClick);
                String indexUrl = genSelectable(node, divRule.get(SpiderConstant.INDEX_URL)).get();
                book.setListIndexUrl(indexUrl);
                String lastChapter = genSelectable(node, divRule.get(SpiderConstant.LAST_CHAPTER)).get();
                book.setListLastChapter(lastChapter);
                String lastChapterUrl = genSelectable(node, divRule.get(SpiderConstant.LAST_CHAPTER_URL)).get();
                book.setListLastChapterUrl(lastChapterUrl);
                if (StrUtil.isEmpty(book.getDetailUrl())) {
                    System.err.println("详情页URL获取失败");
                    System.out.println(book);
                    continue;
                } else {
                    //getBookDetail(book, spiderRule, site);
                }
                if (StrUtil.isEmpty(book.getIndexUrl())) {
                    System.err.println("目录页URL获取失败");
                    System.out.println(book);
                    continue;
                } else {
                    getBookIndexAndContent(book, spiderRule, site);
                }
                books.add(book);
            }
        }
        return books;
    }

    /*
        "author":"",
        "bookName":"",
        "commentCount":"",
        "cover":"",
        "desc":"",
        "indexUrl":"",
        "lastChapter":"",
        "lastChapterUrl":"",
        "monthClick":"",
        "score":"",
        "status":"",
        "statusRule":"",
        "timePatten":"",
        "updateTime":"",
        "visitCount":"",
        "weekClick":"",
        "wordCount":""
    * */
    public void getBookDetail(Book book, SpiderRule spiderRule, Site site) {
        Page detailPage = HttpUtil.getPage(book.getDetailUrl(), site);
        Html detailHtml = detailPage.getHtml();
        Map<String, String> bookDetailRule = spiderRule.getBookDetailRule();
        String author = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.AUTHOR)).get();
        String bookName = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.BOOK_NAME)).get();
        String commentCount = genSelectable(detailHtml,bookDetailRule.get("commentCount")).get();
        String cover = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.COVER)).get();
        String desc = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.DESC)).get();
        String indexUrl = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.INDEX_URL)).get();
        String lastChapter = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.LAST_CHAPTER)).get();
        String lastChapterUrl = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.LAST_CHAPTER_URL)).get();
        String monthClick = genSelectable(detailHtml,bookDetailRule.get(SpiderConstant.MONTH_CLICK)).get();
        System.out.println("author = " + author);
        System.out.println("bookName = " + bookName);
        System.out.println("commentCount = " + commentCount);
        System.out.println("cover = " + cover);
        System.out.println("desc = " + desc);
        System.out.println("indexUrl = " + indexUrl);
        System.out.println("lastChapter = " + lastChapter);
        System.out.println("lastChapterUrl = " + lastChapterUrl);
        System.out.println("monthClick = " + monthClick);
    }

    public void getBookIndexAndContent(Book book, SpiderRule spiderRule, Site site) {
        System.out.println("开始爬取目录");
        List<BookIndex> bookIndices = new ArrayList<>();
        for (BookIndex bookIndex : bookIndices) {
            System.out.println(bookIndex);
            System.out.println("开始爬取内容页");
        }
    }

    /*
     *
     * */
    public List<String> listPageUrl(int spiderSourceId, int catId, Site site) {
        List<String> urls = new ArrayList<>();
        SpiderSource spiderSource = spiderSourceMapper.selectById(spiderSourceId);
        SpiderRule spiderRule = null;
        try {
            spiderRule = objectMapper.readValue(spiderSource.getSpiderRule(), SpiderRule.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        assert spiderRule != null;
        List<String> rules = spiderRule.getCatIdRule().get(catId);
        for (String rule : rules) {
            // split[0] is url,other are Selectable
            String[] split = rule.split(SpiderConstant.SEMICOLON);
            String listUrl = "";
            List<String> selRules = new ArrayList<>();
            for (int i = 0; i < split.length; i++) {
                if (i == 0) {
                    listUrl = split[i];
                } else {
                    selRules.add(split[i]);
                }
            }
            Page page = HttpUtil.getPage(listUrl.replace("{page}", "1"), site);
            Selectable pageCountSel = genSelectable(page.getHtml(), selRules.toArray(new String[selRules.size()]));
            Integer pageCount = ReUtil.getFirstNumber(pageCountSel.get());
            List<String> listUrls = new ArrayList<>(pageCount);
            for (int i = 1; i <= pageCount; i++) {
                listUrls.add(listUrl.replace("{page}", Integer.toString(i)));
            }
            urls.addAll(listUrls);
        }
        return urls;
    }

    /**
     *
     * @param baseSelectable
     * @param selRules
     * @return
     */
    public Selectable genSelectable(Selectable baseSelectable, String... selRules) {
        if (selRules.length <= 0 || "".equals(selRules[0])) {
            return new Html("").xpath("null");
        }
        for (String selRule : selRules) {
            String[] re = selRule.split(SpiderConstant.COMMA);
            switch (re[0]) {
                case "1":
                    baseSelectable = baseSelectable.xpath(re[1]);
                    break;
                case "2":
                    baseSelectable = baseSelectable.$(re[1]);
                    break;
                case "3":
                    baseSelectable = baseSelectable.css(re[1]);
                    break;
                case "4":
                    baseSelectable = baseSelectable.smartContent();
                    break;
                case "5":
                    baseSelectable = baseSelectable.links();
                    break;
                case "6":
                    baseSelectable = baseSelectable.regex(re[1]);
                    break;
                case "7":
                    baseSelectable = baseSelectable.jsonPath(re[1]);
                    break;
                case "8":
                    String[] replacements = re[1].split("-->");
                    if (replacements.length == 1) {
                        baseSelectable = baseSelectable.replace(replacements[0], "");
                    } else {
                        baseSelectable = baseSelectable.replace(replacements[0], replacements[1]);
                    }
                    break;
            }
        }
        return baseSelectable;
    }

    public Selectable genSelectable(Selectable baseSelectable, String selRules) {
        return genSelectable(baseSelectable, selRules.split(SpiderConstant.SEMICOLON));
    }

}
